// 用一个全局变量存储被注册的副作用函数
let activeEffect
// effect栈
const effectStack = []
function effect(fn) {
  const effectFn = () => {
    // 调用cleanup函数完成清除工作
    cleanup(effectFn)
    // 当effectFn执行时，将其设置为当前激活的副作用函数
    activeEffect = effectFn
    // 在调用副作用函数之前将当前副作用函数压入栈中
    effectStack.push(effectFn)
    fn()
    // 在当前副作用函数执行完毕后，将当前副作用函数弹出栈，并把activeEffect还原为之前的值
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  //activeEffect.deps用来存储所有与该副作用函数相关联的依赖集合
  effectFn.deps = []
  //执行副作用函数
  effectFn()
}
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}
// 存储副作用函数
const bucket = new WeakMap()
// 原始数据
const data = { text: 'hello', name: 'zzd', age: 18 }
// 对原始数据的代理
const obj = new Proxy(data, {
  get(target, key, receiver) {
    track(target, key)
    return target[key]
  },
  set(target, key, newVal, receiver) {
    target[key] = newVal
    trigger(target, key)
  },
})

function track(target, key) {
  // 没有activeEffect直接return
  if (!activeEffect) return target[key]
  // 根据target从WeakMap中取得depsMap，他也是一个Map类型
  let depsMap = bucket.get(target)
  // 如果不存在depsMap，那么新建一个Map并与target关联
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  // 再根据key从depsMap中取得deps，它是一个Set类型
  // 里面存储着所有与当前key关联的副作用函数：effects
  let deps = depsMap.get(key)
  // 如果deps不存在，同样新建一个set并与key关联
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  // 最后将当前激活的副作用函数放入deps
  deps.add(activeEffect)
  //deps就是一个与当前副作用函数存在联系的依赖集合
  //将其添加到activeEffect.deps数组中
  activeEffect.deps.push(deps) //新增
}
function trigger(target, key) {
  //根据target从bucket中取得depsMap，它是key-->effects
  const depsMap = bucket.get(target)
  if (!depsMap) return
  //根据key取得所有副作用函数effects
  const effects = depsMap.get(key)
  //执行副作用函数
  // effects && effects.forEach(fn => fn())
  const effectsToRun = new Set(effects)
  effects &&
    effects.forEach((effectFn) => {
      // 如果trigger触发执行的的副作用函数与当前正在执行的副作用函数相同，则不触发执行
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn)
      }
    })
  effectsToRun.forEach((effectFn) => effectFn())
}
effect(() => {
  let a = obj.text,
    b = obj.name,
    c = obj.age
  console.log('副作用函数执行了')
})
setTimeout(() => {
  obj.text = 'dsg'
  obj.name = 'zzd2'
  obj.age = 20
}, 1000)
